﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
namespace PrisonStep
{
    public class BoundingCylinder
    {
        #region Member Variables

        /// <summary>
        /// The height of our bounding cylinder
        /// </summary>
        private float mHeight;

        /// <summary>
        /// The radius of our bounding cylinder
        /// </summary>
        private float mRadius;

        /// <summary>
        /// This point is at the bottom center of the bounding cylinder
        /// </summary>
        private Vector3 mLocation;

        #endregion

        #region Properties

        public Vector3 Location { get { return mLocation; } set { mLocation = value; } }

        #endregion

        /// <summary>
        /// The constructor for an object of type BoundingCylinder
        /// </summary>
        /// <param name="aPosition">This is the bottom center of the bounding cylinder</param>
        /// <param name="aHeight"></param>
        /// <param name="aRadius"></param>
        public BoundingCylinder(Vector3 aPosition, float aHeight, float aRadius)
        {
            this.mHeight = aHeight;
            this.mRadius = aRadius;
            this.mLocation = aPosition;
        }

        /// <summary>
        /// Checks for the intersection between the current bounding cylinder
        /// and a bounding sphere
        /// </summary>
        /// <param name="aBoundingSphere"></param>
        /// <returns></returns>
        public bool Intersect(BoundingSphere aBoundingSphere)
        {
            float lCylinderMinY = mLocation.Y;
            float lCylinderMaxY = mLocation.Y + mHeight;

            // If the center of the sphere is above the cylinder we still need to check if the 
            // the cylinder is within range of the 
            if (aBoundingSphere.Center.Y > lCylinderMaxY)
            {
                Vector3 lDirection = new Vector3(aBoundingSphere.Center.X - mLocation.X, 0, aBoundingSphere.Center.Z - mLocation.Z);
                lDirection.Normalize();

                //This is the closest point we can have on the cylinder to the sphere
                Vector3 lClosestPoint = new Vector3(mLocation.X + lDirection.X * mRadius, lCylinderMaxY, mLocation.Z + lDirection.Z * mRadius);
                if (Vector3.Distance(aBoundingSphere.Center, lClosestPoint) <= mRadius)
                    return true;
            }
            else if (aBoundingSphere.Center.Y < lCylinderMinY)
            {
                Vector3 lDirection = new Vector3(aBoundingSphere.Center.X - mLocation.X, 0, aBoundingSphere.Center.Z - mLocation.Z);
                lDirection.Normalize();

                //This is the closest point we can have on the cylinder to the sphere
                Vector3 lClosestPoint = new Vector3(mLocation.X + lDirection.X * mRadius, lCylinderMinY, mLocation.Z + lDirection.Z * mRadius);
                if (Vector3.Distance(aBoundingSphere.Center, lClosestPoint) <= mRadius)
                    return true;

            }
            // The sphere is within the height of the cylinder
            else
            {
                if (Vector3.Distance(new Vector3(aBoundingSphere.Center.X, 0, aBoundingSphere.Center.Z), new Vector3(mLocation.X, 0, mLocation.Z)) <= mRadius + aBoundingSphere.Radius)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Checks to see if a ray intersects the bounding cylinder
        /// </summary>
        /// <param name="aRay"></param>
        /// <returns></returns>
        public bool Intersect(Ray aRay)
        {
            float lDiffX = aRay.Position.X - mLocation.X;
            float lDiffZ = aRay.Position.Z - mLocation.Z;

            float a = aRay.Direction.X * aRay.Direction.X + aRay.Direction.Z * aRay.Direction.Z;
            float b = 2 * (aRay.Direction.X * lDiffX + aRay.Direction.Z * lDiffZ);
            float c = lDiffX * lDiffX + lDiffZ * lDiffZ - (mRadius * mRadius);

            float t1 = (-b + (float)Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
            float t2 = (-b - (float)Math.Sqrt(b * b - 4 * a * c)) / (2 * a);

            //our t value is the one that is closest to us, hence the ternary operator
            // if t1 < t2, then t = t1 otherwise it equals t2.
            float t = (t1 < t2) ? t1 : t2;

            if (t >= 0)
                return true;
            return false;
        }

        /// <summary>
        /// Checks to see if a ray intersects the bounding cylinder.
        /// Also provides at what value t the ray intersects our cylinder.
        /// </summary>
        /// <param name="aRay"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public bool Intersect(Ray aRay, ref float t)
        {

            float lDiffX = aRay.Position.X - mLocation.X;
            float lDiffZ = aRay.Position.Z - mLocation.Z;

            float a = aRay.Direction.X * aRay.Direction.X + aRay.Direction.Z * aRay.Direction.Z;
            float b = 2 * (aRay.Direction.X * lDiffX + aRay.Direction.Z * lDiffZ);
            float c = lDiffX * lDiffX + lDiffZ * lDiffZ - (mRadius * mRadius);

            float t1 = (-b + (float)Math.Sqrt(b * b - 4 * a * c)) / (2 * a);
            float t2 = (-b - (float)Math.Sqrt(b * b - 4 * a * c)) / (2 * a);


            t = (t1 < t2) ? t1 : t2;

            if (t >= 0)
                return true;
            return false;
        }

    }
}
